package org.pfc.porlapatilla.services;

import java.util.ArrayList;
import java.util.List;

import org.pfc.porlapatilla.DTO.ProductDTO;
import org.pfc.porlapatilla.model.Category;
import org.pfc.porlapatilla.model.Product;
import org.pfc.porlapatilla.model.User;
import org.pfc.porlapatilla.repositories.AdvancedRepository;
import org.pfc.porlapatilla.repositories.CategoryRepository;
import org.pfc.porlapatilla.repositories.ProductRepository;
import org.pfc.porlapatilla.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.Metrics;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.stereotype.Service;

/**
 * This service provides data management to ProductController.
 * 
 * @author jorgepolobermudez
 * 
 */
@Service
public class ProductService {

    /**
     * The {@link ProductRepository} instance.
     */
    @Autowired
    private ProductRepository productRepository;

    /**
     * The {@link AdvancedProductRepository} instance.
     */
    @Autowired
    private AdvancedRepository advancedProductRepository;

    /**
     * The {@link CategoryRepository} instance.
     */
    @Autowired
    private CategoryRepository categoryRepository;

    /**
     * The {@link UserRepository} instance
     */
    @Autowired
    private UserRepository userRepository;

    /**
     * Find all stored products.
     * 
     * @return The list of products.
     */
    public List<Product> findAll() {
	return productRepository.findAll();
    }

    /**
     * Find all stored products.
     * 
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of <strong>size</strong> products in the page
     *         <strong>page</strong>.
     */
    public List<Product> findAll(int page, int size) {
	Page<Product> products = productRepository.findAll(new PageRequest(
		page, size));
	return products.getContent();
    }

    /**
     * Find stored productDTO by id.
     * 
     * @param id
     *            Product id.
     * @return The found productDTO.
     */
    public ProductDTO findById(String id) {
	Product product = productRepository.findById(id);
	return product.toDTODetail();
    }

    /**
     * Find stored products by title.
     * 
     * @param title
     *            Product title.
     * @return The list of products.
     */
    public List<Product> findByTitle(String title) {
	return productRepository.findByTitle(title);
    }

    /**
     * Find stored products by title.
     * 
     * @param title
     *            Product title.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of <strong>size</strong> products in the page
     *         <strong>page</strong>.
     */
    public List<Product> findByTitle(String title, int page, int size) {
	Page<Product> products = productRepository.findByTitle(title,
		new PageRequest(page, size));
	return products.getContent();
    }

    /**
     * Find stored products by title regex.
     * 
     * @param title
     *            Product title regex.
     * @return The list of products.
     */
    public List<Product> findByTitleRegex(String title) {
	return productRepository.findByTitleRegex(title);
    }

    /**
     * Find stored products by title regex.
     * 
     * @param title
     *            Product title regex.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of <strong>size</strong> products in the page
     *         <strong>page</strong>.
     */
    public List<Product> findByTitleRegex(String title, int page, int size) {
	Page<Product> products = productRepository.findByTitleRegex(title,
		new PageRequest(page, size));
	return products.getContent();
    }

    /**
     * Find stored products by category.
     * 
     * @param category
     *            Product category.
     * @return The list of products.
     */
    public List<Product> findByCategory(String category) {
	Category objCategory = categoryRepository.findByName(category);
	return advancedProductRepository.findBySubcategoryIn(objCategory
		.getSubcategories());
    }

    /**
     * Find stored products by category.
     * 
     * @param category
     *            Product category.
     * @param page
     *            Page number
     * @param size
     *            Size of page
     * @return The list of <strong>size</strong> products in the page
     *         <strong>page</strong>.
     */
    public List<Product> findByCategory(String category, int page, int size) {
	Category objCategory = categoryRepository.findByName(category);
	return advancedProductRepository.findBySubcategoryIn(
		objCategory.getSubcategories(), page, size);
    }

    /**
     * Find stored products by subcategory.
     * 
     * @param subcategory
     *            Product subcategory.
     * @return The list of products.
     */
    public List<Product> findBySubcategory(String subcategory) {
	return productRepository.findBySubcategory(subcategory);
    }

    /**
     * Find stored products by subcategory.
     * 
     * @param subcategory
     *            Product subcategory.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of products.
     */
    public List<Product> findBySubcategory(String subcategory, int page,
	    int size) {
	Page<Product> products = productRepository.findBySubcategory(
		subcategory, new PageRequest(page, size));
	return products.getContent();
    }

    /**
     * Find stored products when the distance from a location is less than the
     * distance parameter.
     * 
     * @param longitude
     *            The longitude of location.
     * @param latitude
     *            The latitude of location.
     * @param dist
     *            The distance.
     * @return The list of products.
     */
    public List<Product> findByLocationNear(double latitude, double longitude,
	    double dist) {
	Point point = new Point(latitude, longitude);
	Distance distance = new Distance(dist, Metrics.KILOMETERS);

	return productRepository.findByLocationNear(point, distance);
    }

    /**
     * Find stored products when the distance from a location is less than the
     * distance parameter.
     * 
     * @param longitude
     *            The longitude of location.
     * @param latitude
     *            The latitude of location.
     * @param dist
     *            The distance.
     * @param page
     *            Page number
     * @param size
     *            Size of page
     * @return The list of products.
     */
    public List<Product> findByLocationNear(double latitude, double longitude,
	    double dist, int page, int size) {

	Point point = new Point(latitude, longitude);
	Distance distance = new Distance(dist, Metrics.KILOMETERS);
	Page<Product> products = productRepository.findByLocationNear(point,
		distance, new PageRequest(page, size));
	return products.getContent();
    }

    /**
     * Store a <strong>product</strong>.
     * 
     * @param productDTO
     *            The <strong>product</strong> to store.
     * @return The stored <strong>product</strong> id.
     */
    public String store(ProductDTO productDTO) {
	Product product = new Product();

	product.setAddress(productDTO.getAddress());
	product.setDescription(productDTO.getDescription());
	product.setEnable(true);
	product.setId(productDTO.getProductId());
	product.setPublicationDate(productDTO.getPublicationDate());
	product.setStatus(productDTO.getStatus());
	product.setSubcategory(productDTO.getSubcategory());
	product.setTitle(productDTO.getTitle());
	product.setCommentList(productDTO.getCommentList());
	product.setHasImage(productDTO.getHasImage());
	product.setLocation(productDTO.getAddress().getLocation());

	User provider = userRepository.findByNick(productDTO.getProviderNick());
	if (provider == null) {
	    throw new IllegalArgumentException("Provider user not found");
	}
	product.setProvider(provider);

	return productRepository.save(product).getId();
    }

    /**
     * Find stored productsDTO by state.
     * 
     * @param state
     *            Product state.
     * @return The list of productsDTO.
     */
    public List<ProductDTO> findByStatus(String status) {
	List<Product> products = productRepository.findByStatus(status);
	ArrayList<ProductDTO> productDTOs = new ArrayList<ProductDTO>(
		products.size());
	for (Product product : products) {
	    productDTOs.add(product.toDTOList());
	}
	return productDTOs;
    }

    /**
     * Find stored productsDTO by state.
     * 
     * @param state
     *            Product state.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of productDTO.
     */
    public List<ProductDTO> findByStatus(String status, int page, int size) {
	Page<Product> products = productRepository.findByStatus(status,
		new PageRequest(page, size, Sort.Direction.DESC,
			"publicationDate"));
	ArrayList<ProductDTO> productDTOs = new ArrayList<ProductDTO>(products
		.getContent().size());
	for (Product product : products.getContent()) {
	    productDTOs.add(product.toDTOList());
	}
	return productDTOs;
    }

    /**
     * Find stored productsDTO for search params.
     * 
     * @param text
     *            The text to find in product title.
     * @param subcategories
     *            The subcategory list.
     * @param city
     *            The city product.
     * @param locality
     *            The city product.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of productDTO.
     */
    public List<ProductDTO> findForSearch(String text, String status,
	    List<String> subcategories, String city, String locality,
	    Double latitude, Double longitude, Integer distance, int page,
	    int size) {
	List<Product> products = advancedProductRepository.findForSearch(text, status,
		subcategories, city, locality, latitude, longitude, distance, page, size);
	ArrayList<ProductDTO> productDTOs = new ArrayList<ProductDTO>(
		products.size());
	for (Product product : products) {
	    productDTOs.add(product.toDTOList());
	}
	return productDTOs;
    }

    /**
     * Find all products stored by <strong>provider</strong>.
     * 
     * @param provider
     *            Product provider nick.
     * @param page
     *            Page number.
     * @param size
     *            Size of page.
     * @return The list of <strong>size</strong> products stored by
     *         <strong>provider</strong> in the page <strong>page</strong>.
     */
    public List<ProductDTO> findByProvider(String provider, int page, int size) {
	String providerId = userRepository.findByNick(provider).getId();

	Page<Product> products = productRepository.findByProviderId(providerId,
		new PageRequest(page, size, Sort.Direction.DESC,
			"publicationDate"));
	ArrayList<ProductDTO> productDTOs = new ArrayList<ProductDTO>(products
		.getContent().size());
	for (Product product : products.getContent()) {
	    productDTOs.add(product.toDTOList());
	}
	return productDTOs;
    }
}
